package com.xinsoft.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.entity.dto.ExcelErrRowDTO;
import com.xinsoft.entity.dto.SysMaterialImportDTO;
import com.xinsoft.entity.dto.SysMaterialParam;
import com.xinsoft.entity.po.*;
import com.xinsoft.entity.vo.SysMaterialDetailVo;
import com.xinsoft.entity.vo.SysMaterialVo;
import com.xinsoft.mapper.SysMaterialMapper;
import com.xinsoft.service.*;
import com.xinsoft.utils.ExcelUtils;
import com.xinsoft.validator.ValidatorUtils;
import org.apache.commons.lang.StringUtils;
import org.json.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* @author xuhb
* @description 针对表【sys_material(物料)】的数据库操作Service实现
* @createDate 2023-05-23 13:14:54
*/
@Service
public class SysMaterialServiceImpl extends ServiceImpl<SysMaterialMapper, SysMaterial>
    implements SysMaterialService{

    @Resource
    SysMaterialMapper sysMaterialMapper;

    @Resource
    SysFileHisService sysFileHisService;

    @Resource
    SysMaterialTypeService sysMaterialTypeService;


    @Autowired
    private SysWarehouseItemService warehouseItemService;

    @Autowired
    private SysWarehouseService warehouseService;

    @Autowired
    private SysSupplierService sysSupplierService;

    @Autowired
    private SysDictionaryService sysDictionaryService;



    @Override
    public IPage<SysMaterialVo> listPageMaterial(SysMaterialParam param, HttpServletRequest request) {
        //分页
        Page<SysMaterialVo> page = new Page<SysMaterialVo>(param.getCurrentPage(), param.getPageSize());
        QueryWrapper<SysMaterialVo> queryWrapper = new QueryWrapper<>();
        //查询条件
        queryWrapper.like(StringUtils.isNotEmpty(param.getMaterialNo()),"material_no",param.getMaterialNo())
                .like(StringUtils.isNotEmpty(param.getMaterialName()),"material_name",param.getMaterialName())
                .like(StringUtils.isNotEmpty(param.getMaterialSpecific()),"material_specific",param.getMaterialSpecific())
                .eq(param.getMaterialAttrId()!=null,"material_attr_id",param.getMaterialAttrId())
                .eq(param.getMaterialTypeId()!=null,"material_type_id",param.getMaterialTypeId())
                .eq(param.getAuditStatus()!=null,"audit_status",param.getAuditStatus())
                .eq("do_delete",0)
                .orderByDesc("create_time");
        if (param.getCreateStartTime()!=null){
            queryWrapper.ge("create_time", param.getCreateStartTime());
        }
        if (param.getCreateEndTime()!=null){
            queryWrapper.le("create_time", param.getCreateEndTime());
        }

        IPage<SysMaterialVo> applicationPageList = this.baseMapper.getMaterialList(page, queryWrapper);

        //返回值
        return applicationPageList;
    }

    @Override
    public String saveMaterial(SysMaterial material) {
        LambdaQueryWrapper<SysMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysMaterial::getMaterialNo,material.getMaterialNo())
                .or().eq(SysMaterial::getMaterialName,material.getMaterialName());
        List<SysMaterial> typeList = sysMaterialMapper.selectList(queryWrapper);
        if(typeList!=null&&typeList.size()>0){
            return "物料编码与物料名称均不可以重复";
        }else{
            this.save(material);
            return "";
        }
    }

    @Override
    public String editMaterial(SysMaterial material) {
        LambdaQueryWrapper<SysMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ne(material.getId()!=null,SysMaterial::getId,material.getId());
        queryWrapper.and((wrapper)->{
            wrapper.eq(SysMaterial::getMaterialNo,material.getMaterialNo())
                    .or().eq(SysMaterial::getMaterialName,material.getMaterialName());
                }
        );
        List<SysMaterial> typeList = sysMaterialMapper.selectList(queryWrapper);
        if(typeList!=null&&typeList.size()>0){
            return "物料编码与物料名称均不可以重复";
        }else{
            this.updateById(material);
            return "";
        }
    }

    @Override
    public void deleteMaterial(List<Integer> ids) {
        this.removeByIds(ids);
    }

    @Override
    public SysMaterialDetailVo getMaterialDetail(Integer id) {
        return sysMaterialMapper.getMaterialDetailById(id);
    }



    @Override
    public JSONObject importExcel(MultipartFile multipartFile, HttpServletResponse response) throws IOException {
        // 初始化错误行信息list，用来记录导入失败的rowNum
        List<ExcelErrRowDTO> errRowDOs = new ArrayList<>();

        // 对excel是否为空进行check
        if (multipartFile.isEmpty()) {
            throw new BDException("导入失败，请选择文件！");
        }

        //设置excel参数
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        params.setHeadRows(1);
        List<SysMaterialImportDTO> sysMaterialDTOList;
        List<SysMaterial> sysMaterials = CollectionUtil.newArrayList();

        JSONObject result = new JSONObject();

        // 解析excel中的行，放到sysMaterialList中
        try {
            // 解析excel
            sysMaterialDTOList = ExcelImportUtil.importExcel(multipartFile.getInputStream(), SysMaterialImportDTO.class, params);
        } catch (Exception e) {
            throw new BDException("导入失败，文件内容解析错误！");
        }

        //校验
        if (sysMaterialDTOList.size() > 0) {
            //取出excel和db中所有重复的name
            List<String> excelMaterialNames = sysMaterialDTOList.stream()
                    .map(SysMaterialImportDTO::getMaterialName)
                    .collect(Collectors.toList()); // 获取excel中所有的username
            List<String> dbMaterialNames = this.getAllName();//获取系统中所有的MaterialName
            List<String> duplicateMaterialNames = new ArrayList<>(); // 存放存在重复的clientName，用于定位重复存放存在重复的clientName的行

            if (dbMaterialNames.size() > 0 && excelMaterialNames.size() > 0) {
                dbMaterialNames.addAll(excelMaterialNames);//合并
                duplicateMaterialNames = ExcelUtils.getDuplicateElements(dbMaterialNames);//取出存在重复的username
            }

            //取出excel和db中所有重复的code
            List<String> excelMaterialCodes = sysMaterialDTOList.stream()
                    .map(SysMaterialImportDTO::getMaterialNo)
                    .collect(Collectors.toList()); // 获取excel中所有的username
            List<String> dbMaterialCodes = this.getAllCode();//获取系统中所有的MaterialNo
            List<String> duplicateMaterialCodes = new ArrayList<>(); // 存放存在重复的MaterialNo，用于定位重复存放存在重复的clientName的行

            if (dbMaterialCodes.size() > 0 && excelMaterialCodes.size() > 0) {
                dbMaterialCodes.addAll(excelMaterialCodes);//合并
                duplicateMaterialCodes = ExcelUtils.getDuplicateElements(dbMaterialCodes);//取出存在重复的username
            }


            //物料类型（materialType
            List<SysMaterialType> materialTypeList =  sysMaterialTypeService.list();
            Map<String, Integer> materialTypeMap = materialTypeList.stream()
                    .collect(Collectors.toMap(SysMaterialType::getMtypeName, SysMaterialType::getId, (v1, v2) -> v2));

            //默认仓库 defaultWhName
            List<SysWarehouse> warehouseList =  warehouseService.list();
            Map<String, Integer> warehouseMap = warehouseList.stream()
                    .collect(Collectors.toMap(SysWarehouse::getWarehouseName, SysWarehouse::getId, (v1, v2) -> v2));


            //默认仓位
            List<SysWarehouseItem> warehouseItemList = warehouseItemService.list();
            Map<String, Integer> warehouseItemMap = warehouseItemList.stream()
                    .collect(Collectors.toMap(SysWarehouseItem::getWhItemName, SysWarehouseItem::getId, (v1, v2) -> v2));

            //供应商
            List<SysSupplier> supplierList = sysSupplierService.list();
            Map<String, Integer> supplierMap = supplierList.stream()
                    .collect(Collectors.toMap(SysSupplier::getSupplierName, SysSupplier::getId, (v1, v2) -> v2));



            // 物料属性(字典 material_type)
            List<SysDictionary> materialAttrList =  sysDictionaryService.getDictByTypeCode("material_type");
            Map<String, Integer> materialAttrMap = materialAttrList.stream()
                    .collect(Collectors.toMap(SysDictionary::getDicName, SysDictionary::getId, (v1, v2) -> v2));

            // 计量单位(字典 uom_type)
            List<SysDictionary> uomTypeList =  sysDictionaryService.getDictByTypeCode("uom_type");
            Map<String, Integer> uomTypeMap = uomTypeList.stream()
                    .collect(Collectors.toMap(SysDictionary::getDicName, SysDictionary::getId, (v1, v2) -> v2));


            // 计价方法(字典 valuation_method)
            List<SysDictionary> valuationMethod =  sysDictionaryService.getDictByTypeCode("valuation_method");
            Map<String, Integer> valuationMethodMap = valuationMethod.stream()
                    .collect(Collectors.toMap(SysDictionary::getDicName, SysDictionary::getId, (v1, v2) -> v2));



            int index = 1;
            for (SysMaterialImportDTO sysMaterialDTO : sysMaterialDTOList) {

                SysMaterial sysMaterial = new SysMaterial();
                BeanUtils.copyProperties(sysMaterialDTO,sysMaterial);

                index ++;
                // check
                StringBuilder stringBuilder = new StringBuilder();
                ExcelErrRowDTO excelErrRowDO= new ExcelErrRowDTO();

                // 参数校验
                stringBuilder.append(ValidatorUtils.validateExcel(sysMaterialDTO));

                // 检验物料名称 是否重复
                if(duplicateMaterialNames.contains(sysMaterialDTO.getMaterialName())){
                    stringBuilder.append("物料名称已经存在，请勿重复！");
                }
                // 检验物料编号 是否重复
                if(duplicateMaterialCodes.contains(sysMaterialDTO.getMaterialNo())){
                    stringBuilder.append("物料编号已经存在，请勿重复！");
                }

                //物料类型
                if(StringUtils.isNotEmpty(sysMaterialDTO.getMaterialTypeName())){
                    Integer materialTypeId = materialTypeMap.get(sysMaterialDTO.getMaterialTypeName());
                    if(materialTypeId==null){
                        stringBuilder.append("物料类型在系统中不存在！");
                    }
                    sysMaterial.setMaterialTypeId(materialTypeId);
                }

                //默认仓库ID
                if(StringUtils.isNotEmpty(sysMaterialDTO.getDefaultWhName())){
                    Integer defaultWhId = warehouseMap.get(sysMaterialDTO.getDefaultWhName());
                    if(defaultWhId==null){
                        stringBuilder.append("默认仓库在系统中不存在！");
                    }
                    sysMaterial.setDefaultWhId(defaultWhId);
                }

                //默认仓位ID
                if(StringUtils.isNotEmpty(sysMaterialDTO.getDefaultWhItemName())){
                    Integer defaultWhItemId = warehouseItemMap.get(sysMaterialDTO.getDefaultWhItemName());
                    if(defaultWhItemId==null){
                        stringBuilder.append("默认仓位在系统中不存在！");
                    }
                    sysMaterial.setDefaultWhItemId(defaultWhItemId);
                }

                //供应商
                if(StringUtils.isNotEmpty(sysMaterialDTO.getSupplierName())){
                    Integer supplierId = supplierMap.get(sysMaterialDTO.getSupplierName());
                    if(supplierId==null){
                        stringBuilder.append("供应商在系统中不存在！");
                    }
                    sysMaterial.setSupplierId(supplierId);
                }


                //物料属性
                if(StringUtils.isNotEmpty(sysMaterialDTO.getMaterialAttrName())){
                    Integer materialAttrId = materialAttrMap.get(sysMaterialDTO.getMaterialAttrName());
                    if(materialAttrId==null){
                        stringBuilder.append("物料属性在系统中不存在！");
                    }
                    sysMaterial.setMaterialAttrId(materialAttrId);
                }

                //计价方法
                if(StringUtils.isNotEmpty(sysMaterialDTO.getValuationMethodName())){
                    Integer methodId = valuationMethodMap.get(sysMaterialDTO.getValuationMethodName());
                    if(methodId==null){
                        stringBuilder.append("计价方法在系统中不存在！");
                    }
                    sysMaterial.setValuationMethod(methodId);
                }

                // 工艺路线


                // 是否库存预警 0：否 1：是
                if(StringUtils.equals(sysMaterialDTO.getDoStockWarningName(),"是")){
                    sysMaterial.setDoStockWarning(1);
                }else{
                    sysMaterial.setDoStockWarning(0);
                }

                // 是否进行保质期管理 0：否 1：是
                if(StringUtils.equals(sysMaterialDTO.getDoExpWarningName(),"是")){
                    sysMaterial.setDoExpWarning(1);
                }else{
                    sysMaterial.setDoExpWarning(0);
                }

                // 是否进行批次管理 0：否 1：是
                if(StringUtils.equals(sysMaterialDTO.getDoLotName(),"是")){
                    sysMaterial.setDoLot(1);
                }else{
                    sysMaterial.setDoLot(0);
                }

                //计量单位
                if(StringUtils.isNotEmpty(sysMaterialDTO.getMainUnitName())){
                    Integer mainUnitId = uomTypeMap.get(sysMaterialDTO.getMainUnitName());
                    if(mainUnitId==null){
                        stringBuilder.append("计量单位在系统中不存在！");
                    }
                    sysMaterial.setMainUnit(mainUnitId);
                }

                //辅助单位
                if(StringUtils.isNotEmpty(sysMaterialDTO.getViceUnitName())){
                    Integer viceUnit = uomTypeMap.get(sysMaterialDTO.getViceUnitName());
                    if(viceUnit==null){
                        stringBuilder.append("辅助单位在系统中不存在！");
                    }
                    sysMaterial.setViceUnit(viceUnit);
                }

                // 将错误行信息保存到list
                String errMsg = stringBuilder.toString();
                if(StringUtils.isNotEmpty(errMsg)){
                    excelErrRowDO.setRowIndex(index);
                    excelErrRowDO.setErrMessage(errMsg);
                    errRowDOs.add(excelErrRowDO);
                    continue;
                }
                //保存
                sysMaterials.add(sysMaterial);
            }
            saveBatch(sysMaterials);

            // 导出错误行
            String errFileUrl = ExcelUtils.exportErrRows(errRowDOs,multipartFile,sysFileHisService);
            result.put("errFileUrl",errFileUrl);
        }
        return result;
    }


    private List<String> getAllName() {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.select("material_name");
        List<SysMaterial> list = this.list();
        List<String> names = list.stream().map(SysMaterial::getMaterialName).collect(Collectors.toList());
        return names;
    }

    private List<String> getAllCode() {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.select("material_no");
        List<SysMaterial> list = this.list();
        List<String> codes = list.stream().map(SysMaterial::getMaterialNo).collect(Collectors.toList());
        return codes;
    }

    /**
     * @Return list.get(0) 单价精度
     * @Return list.get(1) 数量精度
     */
    @Override
    public List<Map<Integer, Integer>> getPrecision(List<Integer> ids) {
        // 0 单价精度 1 数量精度 2 重量精度
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.in("id",ids);
        List<SysMaterial> materielList = this.list(queryWrapper);
        List<Map<Integer, Integer>> list = Lists.newArrayList();
        Map<Integer, Integer> priceMap = materielList.stream().collect(Collectors.toMap(SysMaterial::getId, e -> e.getPricePrecision() != null ? e.getPricePrecision() : 0));
        Map<Integer, Integer> quantityMap = materielList.stream().collect(Collectors.toMap(SysMaterial::getId, e -> e.getNumPrecision() != null ? e.getNumPrecision() : 0));
        list.add(priceMap);
        list.add(quantityMap);
        return list;
    }

    @Override
    public void insertOrUpdateBatch(List<SysMaterial> sysMaterialList) {
        sysMaterialMapper.insertOrUpdateBatch(sysMaterialList);
    }

}




